package megrez.job.common;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import lombok.experimental.UtilityClass;
import megrez.job.common.OsHelper.OsType;

/**
 * shell工具
 * 
 * @author Lucky Yang
 * @since 0.0.1
 */
@UtilityClass
public class ShellHelper {

    public int execute(List<String> commands) throws IOException, InterruptedException {
        return execute(commands, null, null);
    }

    public int execute(List<String> commands, Consumer<String> logConsumer) throws IOException, InterruptedException {
        return execute(commands, null, logConsumer);
    }

    public int execute(List<String> commands, Consumer<ProcessBuilder> customProcessBuilder,
            Consumer<String> logConsumer)
            throws IOException, InterruptedException {

        if (CollectionUtils.isEmpty(commands)) {
            throw new IllegalArgumentException("Command line is empty");
        }

        List<String> shellLine = getCommand();
        commands.stream()
                // 去掉首尾空格
                .map(String::trim)
                // 过滤空串
                .filter(StringUtils::hasText)
                .forEach(shellLine::add);

        ProcessBuilder builder = new ProcessBuilder(shellLine);
        builder.redirectErrorStream(true);
        if (customProcessBuilder != null) {
            // 客户端自定义配置
            customProcessBuilder.accept(builder);
        }

        if (logConsumer != null) {
            logConsumer.accept("Execute command : " + shellLine);
            logConsumer.accept("Execute environment :" + builder.environment());
        }

        Process process = builder.start();
        try {
            if (logConsumer != null) {
                ProcessHandle processHandle = process.toHandle();
                ProcessHandle.Info processInfo = processHandle.info();
                logConsumer.accept(String.format(
                        "Execute process info : PID=%s, Arguments=%s, Command=%s, Instant=%s, Total CPU duration=%s, User=%s",
                        processHandle.pid(), processInfo.arguments(), processInfo.command(), processInfo.startInstant(),
                        processInfo.totalCpuDuration(), processInfo.user()));

                // 输出
                try (BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    output.lines().forEach(logConsumer::accept);
                }
            }

            int exitCode = process.waitFor();
            if (logConsumer != null) {
                logConsumer.accept("Execute exit code : " + exitCode);
            }
            return exitCode;
        } finally {
            process.destroy();
        }
    }

    private List<String> getCommand() {
        List<String> rslt = new ArrayList<>();
        if (OsType.WINDOWS.equals(OsHelper.OS)) {
            rslt.addAll(List.of("cmd.exe", "/c"));
        } else {
            rslt.addAll(List.of("/bin/sh", "-c"));
        }
        return rslt;
    }

}
